package com.ruiao.car.service;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruiao.car.utils.GsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Tuple;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Slf4j
@Service
public class RedisService {

    @Autowired
    protected JedisPool jedisPool;

    public <T> T get(String key,Class<T> clazz) {
        Jedis jedis = null ;
        T value = null;
        try {
            jedis = jedisPool.getResource();
            String result = jedis.get(key);
            if(StringUtils.isBlank(result)){
                return null;
            }
            if( clazz == String.class){
                return (T)new String(result);
            }
            value = GsonUtils.get().fromJson(result, clazz);
        } catch (Exception e) {
            log.error("redis get error:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return value;
    }

    public <T> T get(String key, TypeReference type){
        Jedis jedis = null ;
        T value = null;
        try {
            jedis = jedisPool.getResource();
            String result = jedis.get(key);
            if(StringUtils.isBlank(result)){
                return null;
            }
            ObjectMapper mapper = new ObjectMapper();
            value = mapper.readValue(result, type);
        } catch (Exception e) {
            log.error("redis get error:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return value;
    }

    public <T> String set(String key, T value,int seconds) {

        Jedis jedis = null ;
        String result = null;
        try {
            jedis = jedisPool.getResource();
            if ( !(value instanceof String) ){
                String gson = GsonUtils.get().toJson(value);
                jedis.setex(key, seconds, gson);
            }else{
                jedis.setex(key, seconds, String.valueOf(value));
            }
        } catch (Exception e) {
            log.error("redis set error:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return result;

    }

    public Long inc(String key) {

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            return jedis.incr(key);
        } catch (Exception e) {
            log.error("redis inc error:", e);
            return null;
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     *  计数器
     * @param key
     * @param expireSecond 过期时间
     * @param limit 最大值
     * @return 在过期时间内超过最大值返回true，没有超过限制返回false
     */
    public Boolean counter(String key,int expireSecond,int limit) {
        Boolean overLimit = null;
        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            String lua = "  local num = redis.call('incr', KEYS[1])         \n" +
                    "   if tonumber(num) == 1 then                      \n" +
                    "\t     redis.call('expire', KEYS[1], ARGV[1])      \n" +
                    "\t     return 1                                    \n" +
                    "   elseif tonumber(num) > tonumber(ARGV[2]) then   \n" +
                    "\t     return 0                                    \n" +
                    "   else                                            \n" +
                    "\t     return 1                                    \n" +
                    "   end                                             \n";
            Object result = jedis.evalsha(jedis.scriptLoad(lua), Arrays.asList(key),
                    Arrays.asList(String.valueOf(expireSecond), String.valueOf(limit) ));
            overLimit =  Integer.parseInt(result.toString()) == 0 ? true:false;
        }catch (Exception e){
            log.error("redis counter error:", e);
        }finally {
            if(jedis != null){
                try {
                    jedis.close();
                }catch (Exception e){
                    log.error("redis counter close error:", e);
                }
            }
        }
        return overLimit;
    }


    public void del(String... key)  {

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.del(key);
        } catch (Exception e) {
            log.error("redis del error:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }

    }

    /**
     * 是否存在key
     * @param key
     * @return
     */
    public boolean exists(String key) {

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            return jedis.exists(key);
        } catch (Exception e) {
            log.error("redis inc error:{}", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return false;

    }


    /**
     * 设置redis过期时间
     */
    public  void expire(String key, int seconds){
        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.expire(key,seconds);
        } catch (Exception e) {
            log.error("redis expire error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    public void lpush(String key,String... value) throws Exception{

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.lpush(key,value);
        } catch (Exception e) {
            log.error("redis lpush error:", e);
            throw new Exception("redis lpush error");
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }




    public void hmset(String key,Map value,int seconds) throws Exception{

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.hmset(key, value);
            jedis.expire(key,seconds);
        } catch (Exception e) {
            log.error("redis hmset error:", e);
            throw new Exception("redis hmset error");
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    public List<String> hmget(String key, String ... fields) throws Exception{

        Jedis jedis = null ;
        List<String> result ;
        try {
            jedis = jedisPool.getResource();
            result = jedis.hmget(key, fields);
        } catch (Exception e) {
            log.error("redis hmget error:", e);
            throw new Exception("redis hmget error");
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return result;
    }

    public String hget(String key, String field) throws Exception{

        Jedis jedis = null ;
        String result ;
        try {
            jedis = jedisPool.getResource();
            result = jedis.hget(key, field);
        } catch (Exception e) {
            log.error("redis hget error:", e);
            throw new Exception("redis hget error");
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return result;
    }

    //  返回整个hashmap
    public Map<String,String> getAllHmSet(String key) {
        Jedis jedis = null;
        Map<String, String> result = null;
        try {
            jedis = jedisPool.getResource();
            result = jedis.hgetAll(key);
        } catch (Exception e) {
            log.error("redis getAllHmSet error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return result;
    }

    //批量添加set集合元素
    public  long sadd(String key, String... ids ){
        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            return jedis.sadd(key,ids);

        } catch (Exception e) {
            log.error("redis addSets error:{}", e);
            return 0;
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    //批量移除set集合元素
    public  long srem(String key, String... ids ){
        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            return jedis.srem(key,ids);
        } catch (Exception e) {
            log.error("redis addSets error:{}", e);
            return 0;
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    //判断value是否在set中
    public  Boolean sismember(String key, String value ){
        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            return jedis.sismember(key,value);
        } catch (Exception e) {
            log.error("redis sismember error:{}", e);
            return null;
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }


    /**
     * 获取set中的所有数据
     * @param key
     * @return
     */
    public Set<String> smembers(String key){
        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            return jedis.smembers(key);
        } catch (Exception e) {
            log.error("redis smembers error:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return null;
    }

    //  向哈希表中添加map数据,并设置有效时长
    public  void addHmSet(String key,int seconds, Map<String,String> map){
        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.hmset(key, map);
            jedis.expire(key,seconds);
        } catch (Exception e) {
            log.error("redis addHmSet error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    //  向哈希表中添加map数据，不设置有效时长
    public  void addHmSet(String key, Map<String,String> map){
        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.hmset(key, map);
        } catch (Exception e) {
            log.error("redis addHmSet error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    public void delSet(String key, String... values ) {

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.srem(key,values);
        } catch (Exception e) {
            log.error("redis addSet error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    //    替换这个set集合
    public  void replaceSets(String key,  String[] ids){
        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            if(jedis.exists(key)){
                jedis.del(key);
            }
            jedis.sadd(key, ids);
        } catch (Exception e) {
            log.error("redis diffSets error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    // 获取两个集合差集并保存到指定集合中
    public  void diffSets(String key1, String key2, String key3){
        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.sdiffstore(key1, key2,key3);
        } catch (Exception e) {
            log.error("redis diffSets error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    // 获取集合的大小
    public  Long getSetSize(String key){
        Jedis jedis = null ;
        Long num = null;
        try {
            jedis = jedisPool.getResource();
            num = jedis.scard(key);
        } catch (Exception e) {
            log.error("redis getSetSize error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return num;
    }

    //    移除并返回集合中的一个随机数
    public String spop(String key){
        Jedis jedis = null ;
        String value = null;
        try {
            jedis = jedisPool.getResource();
            value = jedis.spop(key);
        } catch (Exception e) {
            log.error("redis spop error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return value;
    }

    //  向哈希表中添加数据
    public  void addHSet(String key, String hkey, String hvalue){
        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.hset(key,hkey,hvalue);
        } catch (Exception e) {
            log.error("redis addHSet error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 增加有序数组
     * @param key
     * @param score
     * @param member
     */
    public void zadd(String key,double score, String member) {

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.zadd(key,score,member);
        } catch (Exception e) {
            log.error("redis zadd error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    public void zaddBatch(String key,Map<String, Double> scoreMembers) {

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.zadd(key,scoreMembers);
        } catch (Exception e) {
            log.error("redis zaddBatch error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    public void zrem(String key,String... value) {

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            jedis.zrem(key,value);
        } catch (Exception e) {
            log.error("redis zrem error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 获取zset中某个成员的分数 成员不存在则返回null
     * @param key
     * @param member
     * @return
     */
    public Double zscore(String key, String member) {

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            return jedis.zscore(key,member);
        } catch (Exception e) {
            log.error("redis zrem error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return null;
    }


    /**
     * 获取zset中某个成员的排名（分数从低到高排名） 成员不存在则返回null
     * @param key
     * @param member
     * @return
     */
    public Long zrank(String key, String member) {

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            return jedis.zrank(key,member);
        } catch (Exception e) {
            log.error("redis zrem error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return null;
    }

    public Boolean zismember(String key, String member) {

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            Long rank =  jedis.zrank(key,member);
            if( rank != null ){
                return true;
            }else{
                return false;
            }
        } catch (Exception e) {
            log.error("redis zismember error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return null;
    }

    /**
     * 获取成员总数
     * @param key
     * @return
     */
    public Long zcard(String key) {

        Jedis jedis = null ;
        try {
            jedis = jedisPool.getResource();
            return jedis.zcard(key);
        } catch (Exception e) {
            log.error("redis zcard error:{}", e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return null;
    }

    /**
     *从有序数组中获取分页数据
     * @param key  集合key
     * @param up  null 为取第一页，true 为向上取最新数据 ，false为向下翻页取数据
     * @param sortValue  排序字段（目前为默认为自增id）
     * @param count  每页数据长度
     * @return
     * 返回有序集key中，score值介于max和min之间(默认包括等于max或min)的所有的成员。有序集成员按score值递减(从大到小)的次序排列。
     */
    public Set<Tuple> zsetPage(String key, Boolean up, String sortValue, int count){
        Jedis jedis = null ;
        Set<Tuple> tuples=null;
        try{
            jedis = jedisPool.getResource();

            //取第一页数据
            if(up == null){
                tuples= jedis.zrevrangeByScoreWithScores(key,"+inf","-inf",0,count);
            }else if( up ){
                //向上取数据
                tuples= jedis.zrevrangeByScoreWithScores(key,"+inf","("+sortValue,0,count);
            }else {
                //向下取数据
                tuples= jedis.zrevrangeByScoreWithScores(key,"("+sortValue,"-inf",0,count);
            }

        }catch (Exception e){
            log.error(" zsetPage error: ",e);
        }finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return tuples;
    }

    public Set<String> zrevrangeByScore(String key, String max, String min){
        Jedis jedis = null ;
        Set<String> tuples=null;
        try{
            jedis = jedisPool.getResource();
            return tuples= jedis.zrevrangeByScore(key,max,min);
        }catch (Exception e){
            log.error(" zsetPage error: ",e);
        }finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return tuples;
    }


    /**
     * 从有序数组中获取分页数据
     *
     * @param key   集合key
     * @param up    null 为取第一页，true 为向上取最新数据 ，false为向下翻页取数据
     * @param id    排序字段（目前为默认为自增id）
     * @param count 每页数据长度
     * @return 返回有序集key中，score值介于max和min之间(默认包括等于max或min)的所有的成员。有序集成员按score值递减(从大到小)的次序排列。
     */
    public Set<Tuple> pageSizeList(String key, Boolean up, Long id, int count) {
        Jedis jedisNew = null;
        Set<Tuple> tuples = null;
        try {
            jedisNew = jedisPool.getResource();

            //取第一页数据
            if (up == null) {
                tuples = jedisNew.zrevrangeByScoreWithScores(key, "+inf", "-inf", 0, count);
            } else if (up == true) {
                //向上取数据
                tuples = jedisNew.zrevrangeByScoreWithScores(key, "+inf", "(" + id, 0, count);
            } else {
                //向下取数据
                tuples = jedisNew.zrevrangeByScoreWithScores(key, "(" + id, "-inf", 0, count);
            }

        } catch (Exception e) {
            log.error("redis pageSizeList error:", e);
        } finally {
            if (jedisNew != null) {
                jedisNew.close();
            }
        }
        return tuples;
    }


    public long delPlus(String key, int maxRetryTimes) {
        Jedis jedis = null;
        long delAck = 0L;
        maxRetryTimes = maxRetryTimes > 20?20:maxRetryTimes;

        try {
            jedis = this.jedisPool.getResource();
            long retryTimes = 0L;

            while(delAck <= 0L && retryTimes < (long)maxRetryTimes) {
                delAck = jedis.del(key).longValue();
                ++retryTimes;
                if(delAck <= 0L) {
                    boolean exists = jedis.exists(key).booleanValue();
                    if(!exists) {
                        break;
                    }

                    log.info("delPlus delAck==0 ,key {},retryTimes {} ", key, Long.valueOf(retryTimes));
                    Thread.sleep(100L * retryTimes);
                }
            }
        } catch (Exception var12) {
            log.error("delPlus del error:", var12);
        } finally {
            if(jedis != null) {
                jedis.close();
            }

        }

        return delAck;
    }




}
